#ifndef __FACTORY_H_
#define __FACTORY_H_

#include "base_core.h"
#include <unordered_map>
#include <string>
#include <functional>
#include <iostream>
#include <memory>

class Factory final
{
    public:
        Factory() = default;
        ~Factory() = default;
        Factory(const Factory&) = default;
        Factory& operator= (const Factory&) = default;

    public:
        std::shared_ptr<BaseCore> CreateCore(std::string key)
        {
            auto it = m_map.find(key);
            if (it == m_map.end())
            {
                std::cout << "key:" << key << " not register yet." << std::endl;
                return nullptr;
            }
            return it->second();
        }

    public:
        template
        <
            typename T,
            typename = typename std::enable_if
                <
                    std::is_base_of<BaseCore, typename std::decay<T>::type>::value 
                >::type 
        >
        class Register_F
        {
            public:
                template
                <
                    typename... Args,
                    typename = typename std::enable_if
                    <
                        std::is_constructible<T, Args...>::value
                    >::type
                >
                Register_F(std::string key, Args&&... args)
                {
                    Factory::m_map[key] = [&]()->std::shared_ptr<BaseCore>{
                        return std::static_pointer_cast<BaseCore>(std::make_shared<T>(std::forward<Args>(args)...));
                    };
                }
        };

    private:
        static std::unordered_map<std::string, std::function<std::shared_ptr<BaseCore>(void)>>  m_map;
};

std::unordered_map<std::string, std::function<std::shared_ptr<BaseCore>(void)>> Factory::m_map;

#define CLASS_NAME(CLASS)   #CLASS 

#define REGISTER_OBJECT_CLASS_KEY(CLASS, KEY, ...) \
    Factory::Register_F<CLASS>  __##CLASS(KEY, ##__VA_ARGS__)

#define REGISTER_OBJECT_CORE(CLASS, ...) \
    REGISTER_OBJECT_CLASS_KEY(CLASS, CLASS_NAME(CLASS), ##__VA_ARGS__)

#endif 
